﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using CncConvProg.Model;
using CncConvProg.Model.ConversationalStructure;
using CncConvProg.Model.ConversationalStructure.Lavorazioni.Common;
using CncConvProg.ViewModel.AuxViewModel;
using CncConvProg.ViewModel.AuxViewModel.TreeViewModel;
using CncConvProg.ViewModel.MVVM_Library;
using GongSolutions.Wpf.DragDrop;
using MecPrev.Resources;

namespace CncConvProg.ViewModel.MainViewModel
{
    /// <summary>
    /// Questa parte gestisce la parte del report.
    /// </summary>
    public partial class ClassModelViewModel
    {
        /*
     * 06/11/2011
     * 
     * Ho dettagli costi fissi, indipendenti dalla quantita
     * e dettagli costi che aumento prop. con qta.
     */
        #region Update Cycle Time Code

        private ObservableCollection<VoceReportTreeview> _reportDetailTreeview;
        public ObservableCollection<VoceReportTreeview> ReportDetailTreeview
        {
            get { return _reportDetailTreeview; }

            set
            {
                _reportDetailTreeview = value;
                OnPropertyChanged("ReportDetailTreeview");
            }
        }


        /// <summary>
        /// Creo treeview con tutti i dettagli dei costi, raggruppati per :
        ///   -> voce principale
        ///     -> voce secondaria
        ///     -> Numero di fase
        /// 
        /// In modo da fare una cosa chiara e di fare una lista di treeviewitem e in seguito usare linq per raggruppare e fare apposite subitem.
        /// Automatizzo la creazione dei totali , calcolandoli successivamente guardando tempi e costi delle foglie.
        /// 
        /// !!-- In alternativa utilizzare metodi aux tipo aggiungivoce(vocePrimaria, tempo, e cosi via..).. cmq provo prima con linq mi sembra più efficace..
        /// // ora come ora penso che il metodo aux sia più elastico..
        /// 
        /// </summary>
        /// <param name="cicleTimeCollector"></param>
        private void UpdateReportTreeView(CycleTimeCollector cicleTimeCollector)
        {
            /*
             * Dichiaro quali saranno le mie voci principali,
             * Creo classe nuova con anonymous type 
             */
            var detailedTreeviw = new List<VoceReportTreeview>();

            /*
             * Prima raccolgo tutte le voci roote e poi assegno sub item
             */

            var stockQuantity = cicleTimeCollector.StockQuantity;

            var costoPerPezzo = 0.0d;
            var costoPerLotto = 0.0d;

            var programmazione = AddVoceTreeView(detailedTreeviw, null, GuiRes.Programmation, new TimeSpan(), 0, 0, stockQuantity);
            var setupMachine = AddVoceTreeView(detailedTreeviw, null, GuiRes.SetupMachine, new TimeSpan(), 0, 0, stockQuantity);
            var loadingMachine = AddVoceTreeView(detailedTreeviw, null, GuiRes.LoadingMachineTv, new TimeSpan(), 0, 0, stockQuantity);
            var machingTime = AddVoceTreeView(detailedTreeviw, null, GuiRes.MachiningTime, new TimeSpan(), 0, 0, stockQuantity);
            var toolsWear = AddVoceTreeView(detailedTreeviw, null, GuiRes.ToolWear, new TimeSpan(), 0, 0, stockQuantity);

            var tempoCambioUtensileTv = AddVoceTreeView(detailedTreeviw, machingTime, GuiRes.ToolChange, new TimeSpan(), 0, 0, stockQuantity);
            var rapidTimeTv = AddVoceTreeView(detailedTreeviw, machingTime, GuiRes.RapidTime, new TimeSpan(), 0, 0, stockQuantity);
            var workTimeTv = AddVoceTreeView(detailedTreeviw, machingTime, GuiRes.WorkTime, new TimeSpan(), 0, 0, stockQuantity);

            var montaggioUtensile = AddVoceTreeView(detailedTreeviw, setupMachine, GuiRes.MountingTools, new TimeSpan(), 0, 0, stockQuantity);
            var setupFixture = AddVoceTreeView(detailedTreeviw, setupMachine, GuiRes.SetupFixtureTv, new TimeSpan(), 0, 0, stockQuantity);

            var materialeVoce = AddVoceTreeView(detailedTreeviw, null, GuiRes.Material, new TimeSpan(), 0, 0, stockQuantity, Singleton.Instance.CostoMaterialeStock);
            var costiVari = AddVoceTreeView(detailedTreeviw, null, GuiRes.VariousCost, new TimeSpan(), 0, 0, stockQuantity, Singleton.Instance.CostiAggiuntiviStock);


            // Aggiungo costo materiale
            costoPerPezzo += Singleton.Instance.CostoMaterialeCad;

            // Voce costi aggiuntivi
            costoPerPezzo += Singleton.Instance.CostiAggiuntiviCad;
            var cnt = 1;


            /*
             * Qui comincio a raccogliere le varie voci senza preoccuparmi della discendenza e dei totali e numero fase
             */
            foreach (var faseDiLavoro in cicleTimeCollector.FasiDiLavoroTime)
            {
                var hourCost = faseDiLavoro.CostoOrarioMacchina;

                //Tempo Programmazione -
                var progVoce = FormatTimeHelper.FormatNumberIterationTime(faseDiLavoro.NumeroOperazioni, faseDiLavoro.MinutiProgrammazioneOperazione, " op");
                var progTime = TimeSpan.FromMinutes(faseDiLavoro.NumeroOperazioni * faseDiLavoro.MinutiProgrammazioneOperazione);

                costoPerLotto += progTime.TotalHours * hourCost;

                AddVoceTreeView(detailedTreeviw, programmazione, progVoce, progTime, cnt, hourCost, stockQuantity);

                //// Setup macchina-Setup Utensili
                var usedTool = faseDiLavoro.GetUsedToolCount();
                var setupVoce = FormatTimeHelper.FormatNumberIterationTime(usedTool,
                                                                          faseDiLavoro.MinutiMontaggioUtensile,
                                                                          " tool");


                var tempoMontaggiUtensili = TimeSpan.FromMinutes(usedTool * faseDiLavoro.MinutiMontaggioUtensile);

                costoPerLotto += tempoMontaggiUtensili.TotalHours * hourCost;

                AddVoceTreeView(detailedTreeviw, montaggioUtensile, setupVoce, tempoMontaggiUtensili, cnt, hourCost, stockQuantity);


                ////Setup macchina - Fixture 
                var setFixTime = TimeSpan.FromMinutes(faseDiLavoro.MinutiPreparazioneStaffaggio);

                costoPerLotto += setFixTime.TotalHours * hourCost;

                AddVoceTreeView(detailedTreeviw, setupFixture, GuiRes.Fixture, setFixTime, cnt, hourCost, stockQuantity);


                //Loading Machine - 
                var voceLoading = FormatTimeHelper.FormatStockTime(TimeSpan.FromSeconds(faseDiLavoro.SecondiCaricamentoMaterialeGrezzo), stockQuantity);
                var loadMatTime = TimeSpan.FromSeconds(faseDiLavoro.SecondiCaricamentoMaterialeGrezzo * stockQuantity);

                costoPerPezzo += TimeSpan.FromSeconds(faseDiLavoro.SecondiCaricamentoMaterialeGrezzo).TotalHours * hourCost;

                AddVoceTreeView(detailedTreeviw, loadingMachine, voceLoading, loadMatTime, cnt, hourCost, stockQuantity);

                //Work Time -- per ora metto solamente tempo macchina
                // poi faro tempo cambio utensile
                // tempo rapido / lavoro
                {
                    var rapidTimeSpan = faseDiLavoro.GetTotalRapidTime();
                    var workTimeSpan = faseDiLavoro.GetTotalWorkTime();
                    var totalChangeToolTime = faseDiLavoro.GetChangeToolTime();

                    var stockRapidTime = FormatTimeHelper.FormatStockTime(rapidTimeSpan, stockQuantity);
                    var stockWorkTime = FormatTimeHelper.FormatStockTime(workTimeSpan, stockQuantity);
                    var stockChangeToolTime = FormatTimeHelper.FormatStockTime(totalChangeToolTime, stockQuantity);

                    AddVoceTreeView(detailedTreeviw, tempoCambioUtensileTv, stockChangeToolTime, TimeSpan.FromHours(totalChangeToolTime.TotalHours * stockQuantity), cnt, hourCost,
                                    stockQuantity);

                    AddVoceTreeView(detailedTreeviw, rapidTimeTv, stockRapidTime, TimeSpan.FromHours(rapidTimeSpan.TotalHours * stockQuantity), cnt, hourCost,
                                    stockQuantity);

                    AddVoceTreeView(detailedTreeviw, workTimeTv, stockWorkTime, TimeSpan.FromHours(workTimeSpan.TotalHours * stockQuantity), cnt, hourCost,
                                    stockQuantity);

                    var totSingleTime = faseDiLavoro.GetTotalMachinigTime();

                    totSingleTime = totSingleTime.Add(totalChangeToolTime);

                    costoPerPezzo += totSingleTime.TotalHours * hourCost;
                }

                // Consumo Utensili 
                var postazioniUtensili = faseDiLavoro.GetListaPostazioniUtensili();

                var faseToolWears = AddVoceTreeView(detailedTreeviw, toolsWear, string.Format("Fase {0} ", cnt), new TimeSpan(), 0, 0,stockQuantity);

                foreach (var i in postazioniUtensili)
                {
                    var toolName = faseDiLavoro.GetToolDescriptionFromNumber(i);

                    var voce = string.Format(" T {0} - {1} ", i, toolName);

                    var usura = faseDiLavoro.GetTotalToolWearFromToolNumber(i);

                    AddVoceTreeView(detailedTreeviw, faseToolWears, voce, faseDiLavoro.GetTotalTimeFromToolNumber(i), 0, 0, stockQuantity, usura * stockQuantity);
                    // AddVoceTreeView(detailedTreeviw, toolsWear, voce, faseDiLavoro.GetTotalTimeFromToolNumber(i), cnt, 0, stockQuantity, usura * stockQuantity);

                    costoPerPezzo += usura;

                }

                cnt++;

            }



            foreach (var treeViewItemViewModel in detailedTreeviw)
            {
                treeViewItemViewModel.IsExpanded = false;
            }

            ReportDetailTreeview = new ObservableCollection<VoceReportTreeview>(detailedTreeviw);

            /*
             * piccolo h.
             * Aggiungo  dettagli costo materiale e aggiuntivi qui in modo che non sia visibile nei dettagli costo
             * ma nelle altre cose si..
             */

            UpdatePieChart(detailedTreeviw);

            UpdatePriceChart(costoPerLotto, costoPerPezzo);

            PrezzoCalcolato = (costoPerLotto + (costoPerPezzo * stockQuantity)) / stockQuantity;


        }

        private static VoceReportTreeview AddVoceTreeView(ICollection<VoceReportTreeview> treeview, VoceReportTreeview parent, string voce, TimeSpan time, int numeroFase, double costoOrario, int stockQuantity, double totale = 0)
        {
            var voceToAdd = new VoceReportTreeview(voce, numeroFase, costoOrario, parent, stockQuantity) { Time = time, Totale = totale };

            if (parent == null)
            {
                treeview.Add(voceToAdd);
            }
            return voceToAdd;
        }

        private void UpdatePriceChart(double costiFissi, double costoSingoloPz)
        {
            var min = LottoQtaMin == 0 ? 1 : LottoQtaMin;

            int max = LottoQtaMax;

            if (max <= min)
                max = min + 1;

            var delta = max - min;

            var numeroRilevazioni = delta;

            var numeroRilevazioniMax = 10;

            if (numeroRilevazioni > numeroRilevazioniMax)
                numeroRilevazioni = numeroRilevazioniMax;

            var step = (int)Math.Round((double)(delta) / numeroRilevazioni, 0);

            var o = new ObservableCollection<KeyValuePair<int, double>>();

            for (var i = 0; i < numeroRilevazioni; i++)
            {
                var lottoQta = step * i + min;

                var totale = (costiFissi + (costoSingoloPz * lottoQta)) / lottoQta;

                o.Add(new KeyValuePair<int, double>(lottoQta, totale));

            }

            PriceChart = o;

        }

        public double PrezzoCalcolato
        {
            get { return Singleton.Instance.PrezzoCalcolto; }

            set
            {
                Singleton.Instance.PrezzoCalcolto = value;
                OnPropertyChanged("PrezzoCalcolato");
                OnPropertyChanged("PrezzoCalcolatoStock");
                OnPropertyChanged("IsSottocosto");
            }
        }
        public double PrezzoCalcolatoStock
        {
            get { return PrezzoCalcolato * StockQuantity; }
        }

        public double PrezzoDefinitivo
        {
            get { return Singleton.Instance.PrezzoDefinitivo; }

            set
            {
                Singleton.Instance.PrezzoDefinitivo = value;
                OnPropertyChanged("PrezzoDefinitivo");
                OnPropertyChanged("PrezzoDefinitoStock");
                OnPropertyChanged("IsSottocosto");

            }
        }

        public double PrezzoDefinitoStock
        {
            get { return PrezzoDefinitivo * StockQuantity; }
        }

        public bool IsSottocosto
        {
            get { return PrezzoDefinitivo < PrezzoCalcolato; }
        }

        public string Note
        {
            get { return Singleton.Instance.Note; }

            set
            {
                Singleton.Instance.Note = value;
                OnPropertyChanged("Note");
            }
        }



        RelayCommand _updateTimeCode;

        ///<summary>
        ///Metodo che aggiorna tempo ciclo.
        /// La macchina la devo sempre prendere da file aggiornato..
        ///</summary>
        private void UpdateTime()
        {
            /*
             * Riaggiornare il riferimeno alle macchine utensili
             * riaggiornare le lavorazioni.
             */
            /*
          * --Prima itera fra tutte le prese ( fasi di lavorazione )
          * -- Ogni Fase ha :
          * - Tempo Setup Utensili = Numero Utensili * TempoMontaggioUtensile
          * - Tempo Programmazione = TempoMedioProgrammazioneOperazione * NumeroOperazioni
          * - Tempo CaricamentoPezzo = NumeroPezzi * TempoCaricamento
          * - Tempo Medio Setup Staffaggio
          * - Tempo Totale Cambio Utensile 
          * 
          * - Ogni fase ha lista di operazioni
          * - Lista operazioni con dettagli
          *    -Ogni Operazione ha :
          *      - Tempo Totale 
          *      - Tempo Rapido
          *      - Tempo Lavoro
          *          
          * - Lista Utensili 
          *   - Ogni utensile ha tempo rapido
          *   - Distanza percorsa totale
          *      - In Rapido
          *      - In Lavoro
          *      - Consumo Money
          *      
          * -- Tralasciando per ora materiale e costi vari..
          * 
          * ho grafico :
          * 
             var costoLavorazioneMacchina = 0.0d;
             var costoTempoAttrezzaggio = 0.0d;
             var costoTempoProgrammazione = 0.0d;
             var costoCaricamentoMacchina = 0.0d;
             var consumoInserti = 0.0d;
          * 
          */

            var model = Singleton.Instance;

            /*
             * todo : aggiungere tempo cambio utensile.
             */
            var fasi = model.GetFasiDiLavoro();

            // Se una o più fasi contengono errori non procedo.
            if (fasi.Any(f => f.IsValid == false))
            {
                MessageBox.Show("Correct Error !", "Correct Error", MessageBoxButton.OK, MessageBoxImage.Warning,
                                MessageBoxResult.OK);
                return;
            }
            // Classe che mi serve per raccogliere tutti i vari tempi
            var timeCollector = new CycleTimeCollector();

            timeCollector.StockQuantity = model.StockQuantity;

            foreach (var faseDiLavoro in fasi)
            {
                var faseTimeCollector = new FaseDiLavoroTime();

                timeCollector.Add(faseTimeCollector);

                faseTimeCollector.SecondiCaricamentoMaterialeGrezzo = faseDiLavoro.MachineLoadingTime;

                var macchina = faseDiLavoro.GetMacchina();

                faseTimeCollector.TempoCambioUtensile = macchina.ChangeToolTime;
                faseTimeCollector.CostoOrarioMacchina = macchina.CostoOrario;
                faseTimeCollector.MinutiPreparazioneStaffaggio = faseDiLavoro.AverageSetupFixtureTime;
                faseTimeCollector.MinutiProgrammazioneOperazione = faseDiLavoro.AverageProgrammingOperationTime;
                faseTimeCollector.MinutiMontaggioUtensile = faseDiLavoro.AverageMountingToolTime;

                var operazioni = model.GetOperationList(faseDiLavoro.FaseDiLavoroGuid);

                faseTimeCollector.NumeroOperazioni = operazioni.Count();

                foreach (var operazione in operazioni)
                {
                    // operazione.UpdateProgramPath(macchina); // lo commento in quanto lo ho appena calcolato..
                    var opTime = operazione.OperationTime;
                    faseTimeCollector.Add(opTime);
                }



                // aggiorno tempi operazioni dei view model operazioni
                var op = OperationList;
                foreach (var operationMainScreenViewModel in op)
                    operationMainScreenViewModel.UpdateTime();


                /*
                 * Una volta "raccolti" tutti i vari tempi dentro classe apposita
                 * creo un treeview in modo che sia comprensibile.
                 */
                UpdateReportTreeView(timeCollector);
            }
        }

        public ICommand UpdateTimeCmd
        {
            get
            {
                return _updateTimeCode ?? (_updateTimeCode = new RelayCommand(param => UpdateTime(),
                                                                              param => !IsBusy));
            }
        }

        #endregion
        private void UpdatePieChart(IEnumerable<TreeViewItemViewModel> rpt)
        {
            var o = new ObservableCollection<KeyValuePair<string, double>>();

            foreach (var treeViewItemViewModel in rpt.OfType<VoceReportTreeview>())
            {

                var v = treeViewItemViewModel.Voce;
                var i = treeViewItemViewModel.Totale;

                if (i > 0)
                {
                    o.Add(new KeyValuePair<string, double>(v, i));
                }
            }

            PieChart = o;

            PieChartTitle = "Lotto " + StockQuantity + " pz ";

            OnPropertyChanged("PieChart");
        }

        public int LottoQtaMin
        {
            get { return Singleton.Instance.LottoQtaMin; }
            set
            {
                Singleton.Instance.LottoQtaMin = value;
                OnPropertyChanged("LottoQtaMin");
            }
        }

        public int LottoQtaMax
        {
            get { return Singleton.Instance.LottoQtaMax; }
            set
            {
                Singleton.Instance.LottoQtaMax = value;
                OnPropertyChanged("LottoQtaMax");
            }
        }

        private string _pieCharTitle;
        public string PieChartTitle
        {
            get { return _pieCharTitle; }
            set
            {
                _pieCharTitle = value;
                OnPropertyChanged("PieChartTitle");
            }
        }

        private ObservableCollection<KeyValuePair<int, double>> _priceChart = new ObservableCollection<KeyValuePair<int, double>>();
        public ObservableCollection<KeyValuePair<int, double>> PriceChart
        {
            get
            {
                return _priceChart;
            }

            set
            {
                _priceChart = value;
                OnPropertyChanged("PriceChart");
            }
        }

        private ObservableCollection<KeyValuePair<string, double>> _pieChart = new ObservableCollection<KeyValuePair<string, double>>();
        public ObservableCollection<KeyValuePair<string, double>> PieChart
        {
            get
            {
                return _pieChart;
            }

            set
            {
                _pieChart = value;
                OnPropertyChanged("PieChart");
            }

        }

        public static class FormatTimeHelper
        {
            public static string GetFormattedPhaseNumber(int phaseNumber)
            {
                return "# " + phaseNumber + " - ";
            }
            public static TimeSpan FormatTime(int minutes)
            {
                var timeSpan = TimeSpan.FromMinutes(minutes);

                return FormatTime(timeSpan);
            }

            public static TimeSpan FormatTime(TimeSpan timeSpan)
            {
                return timeSpan.Subtract(TimeSpan.FromMilliseconds(timeSpan.Milliseconds));
            }



            /// <summary>
            /// Ritorna stringa 
            /// </summary>
            /// <param name="singleLoadingTime"></param>
            /// <param name="stockQuantity"></param>
            /// <returns></returns>
            public static string FormatStockTime(TimeSpan singleLoadingTime, int stockQuantity)
            {
                return " " + singleLoadingTime.Subtract(TimeSpan.FromMilliseconds(singleLoadingTime.Milliseconds)) + " x " + stockQuantity + "pcs = ";
            }

            /// <summary>
            /// Ritorna stringa 
            /// </summary>
            /// <returns></returns>
            public static string FormatNumberIterationTime(int numberOperation, int minutes, string suffisso)
            {
                var s = string.Empty;
                if (numberOperation > 1)
                    s = "s";

                return " " + FormatTime(minutes) + " x " + numberOperation + " " + suffisso + s + " = ";
            }
        }

    }

    public class VoceReportTreeview : TreeViewItemViewModel
    {
        public string VoceParent { get; set; }

        //public string VoceSecondaria { get; set; }

        public int NumeroFase { get; private set; }

        public int StockQuantity { get; private set; }


        public double HourCost { get; private set; }

        public string NumeroFaseFormatted
        {
            get
            {
                if (NumeroFase > 0)
                    return "# " + NumeroFase;

                return string.Empty;
            }
        }
        public bool IsRootItem
        {
            get
            {
                return Parent == null;
            }
        }
        private string _voce;
        public string Voce
        {
            get { return _voce; }
            set
            {
                _voce = value;
                OnPropertyChanged("Voce");
            }
        }
        public double TotaleCad
        {
            get
            {
                if (StockQuantity <= 0) return 0;
                return Totale / StockQuantity;
            }
        }

        private double _totale;
        public double Totale
        {
            get
            {
                // se ho dei children faccio la somma dei children
                if (Children.Count > 0)
                {
                    var c = Children.OfType<VoceReportTreeview>();
                    var t = 00.0d;

                    foreach (var voceReportTreeview in c)
                    {
                        t += voceReportTreeview.Totale;
                    }

                    return t;
                }

                if (_timeSpan != TimeSpan.Zero && HourCost > 0)
                {
                    return _timeSpan.TotalHours * HourCost;
                }

                return _totale;
            }
            set
            {
                _totale = value;
                OnPropertyChanged("Totale");
            }
        }

        public string FormatTime
        {
            get
            {
                if (Time == TimeSpan.Zero)
                    return string.Empty;

                return Time.ToString();
            }
        }
        private TimeSpan _timeSpan;
        public TimeSpan Time
        {
            get
            {
                // se ho dei children faccio la somma dei children
                if (Children.Count > 0)
                {
                    var c = Children.OfType<VoceReportTreeview>();
                    var t = new TimeSpan();

                    foreach (var voceReportTreeview in c)
                    {
                        t = t.Add(voceReportTreeview.Time);
                    }

                    return t;
                }
                return _timeSpan.Subtract(TimeSpan.FromMilliseconds(_timeSpan.Milliseconds));
            }
            set
            {
                _timeSpan = value;
                OnPropertyChanged("Time");
            }
        }
        public VoceReportTreeview(string label, int numerPhase, double hourCost, TreeViewItemViewModel parent, int stockQuantity)
            : base(label, parent)
        {
            StockQuantity = stockQuantity;

            NumeroFase = numerPhase;
            HourCost = hourCost;
            IsExpanded = true;

            Voce = label;
            if (parent != null)
                parent.Children.Add(this);
        }



    }
}



